/*
 * CommonApp: version-0.0.1a
 *
 * Change Logs:
 * Date               Author              Notes
 * 2023-2-12          chenjiaxing         first version
 *
 * Description: specific application for network device, this code implements 
 *              the common operations for any device(as server in the network), used for all link type.
 */
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include "libhash.h"
#include "device_demo.h"
#include "libprotocol.h"
#include "link_socket.h"
#include "zlog.h"
#include "sqlite3.h"
#include "CircularBuffer.h"
#include "device_pth.h"

typedef union u32Union {
	uint8_t bytes[4];
	uint32_t value;
}u32Union_t;

static uint32_t endianSwap(uint32_t data, uint32_t length)
{
	u32Union_t raw, next;
	uint32_t i  = 0;
	raw.value   = data;
    next.value  = 0;
	while(length > 0) {
		next.bytes[i++] = raw.bytes[--length];
	}
	return next.value;
}

static int u32EndianSwap(uint32_t *data)
{
    u32Union_t bigEndianU32 = {0};
    if(!data){
        zlog_error(gZlog, "u32EndianSwap failed, [null ptr]");
        return -1;
    }
    bigEndianU32.value = endianSwap(*data, sizeof(uint32_t));
    *data = bigEndianU32.value;
    return 0;
}

int requirSetNodeType(int devId, acqNodeType_t acqNodeType, int dataCnt)
{
  int ret = -1;
  linkList_t *linkNode = NULL;
  linkNode = findLinkNodeFromList(gServerLinkHead, devId);
  //0. 准备payload
  dataCnt = u32EndianSwap(&dataCnt);
  //1. 发送命令帧
  ret = proFramesSend(&(linkNode->link.chan), &dataCnt, sizeof(int), SPEC_OP1_SET_WORKMODE, acqNodeType, 0);
  if(ret < 0){
    zlog_error(gZlog, "failed to set workmode to device[%d], ret[%d]", devId, ret);
    return ret;
  }
  //2. 等待响应帧
  ret = waitAck(ACK_TIMEOUT_MS);
  if(ret < 0){
    zlog_error(gZlog, "set workmode to device[%d] ack timeout", devId);
    return ret;
  }
  //3. 处理接收内容
  if(/* gDevMsg.chanId != devId || */gDevMsg.op1 != SPEC_OP1_SET_WORKMODE){
    zlog_error(gZlog, "set workmode to device[%d] ack failed", devId);
    return ret;
  }
  return 0;
}
unsigned int gStrainDataCnt;
int requirGetStrain(int devId, strainInfo_t *strainInfo, int nodeDataCnt)
{
  int ret = -1;
  linkList_t *linkNode = NULL;
  linkNode = findLinkNodeFromList(gServerLinkHead, devId);
  zlog_info(gZlog, "sending get strain request");
  //1. 发送命令帧
  ret = proFramesSend(&(linkNode->link.chan), NULL, 0, \
          SPEC_OP1_GET_STRAIN, nodeDataCnt, 0);
  if(ret < 0){
    zlog_error(gZlog, "failed to request strain info from device[%d], ret[%d]", devId, ret);
    return ret;
  }
  //2. 等待响应帧
  ret = waitAck(ACK_TIMEOUT_MS);
  if(ret < 0){
    zlog_error(gZlog, "request strain info from device[%d] timeout", devId);
    return ret;
  }
  //3. 处理接收内容
  if( gDevMsg.chanId != devId && gDevMsg.op1 != SPEC_OP1_GET_STRAIN){
    zlog_error(gZlog, "recv strain info from device[%d] ack failed", devId);
    return -1;
  }
  strainInfo_t getStrainInfo;
  for(int i = 0; i < nodeDataCnt; i++){
    memcpy(&getStrainInfo, (gDevMsg.payload + i * sizeof(strainInfo_t)), sizeof(strainInfo_t));
    strainInfo[i].timeStamp.tv_sec  = ntohl((getStrainInfo.timeStamp.tv_sec & 0xffffffff00000000) >> 32) + ntohl(getStrainInfo.timeStamp.tv_sec & 0xffffffff);
    strainInfo[i].timeStamp.tv_usec = ntohl((getStrainInfo.timeStamp.tv_usec & 0xffffffff00000000) >> 32) + ntohl(getStrainInfo.timeStamp.tv_usec & 0xffffffff);
    u32EndianSwap((uint32_t*)&getStrainInfo.devId);
    u32EndianSwap((uint32_t*)&getStrainInfo.strainAdCh1);
    u32EndianSwap((uint32_t*)&getStrainInfo.strainAdCh2);
    u32EndianSwap((uint32_t*)&getStrainInfo.strainVal);
    strainInfo[i].devId             = getStrainInfo.devId;
    strainInfo[i].strainAdCh1       = getStrainInfo.strainAdCh1;
    strainInfo[i].strainAdCh2       = getStrainInfo.strainAdCh2;
    strainInfo[i].strainVal         = getStrainInfo.strainVal;
    zlog_info(gZlog, "got strain data");
    //4. print info
    struct tm fmtTime = {0};
    localtime_r(&(strainInfo[i].timeStamp.tv_sec), &fmtTime);
    char timeStampStr[32] = {0};
    strftime(timeStampStr, sizeof(timeStampStr), "%Y-%m-%d %H:%M:%S", &fmtTime);
    sprintf(&timeStampStr[19], ".%ld", strainInfo[i].timeStamp.tv_usec / 1000);
    zlog_info(gZlog, "\nrecv strain acqNode[%d] data:, \n\
        time stamp.........[%s], \n\
        strain AD CH1 Val..[%.4f], \n\
        strain AD CH2 Val..[%.4f], \n\
        strain value.......[%.4f]",\
        strainInfo[i].devId, timeStampStr, strainInfo[i].strainAdCh1, \
        strainInfo[i].strainAdCh2, strainInfo[i].strainVal);
    //5. record strain info
    zlog_info(gZlog, "start to record strain data");
    if(strainBufFlag){
      CircularBufferPush(strainCircBuf1, &strainInfo[i], sizeof(strainInfo_t));
      if(i == (nodeDataCnt - 1)){
        strainBufFlag     = true;
        strainRecordFlag  = true;
      }
      zlog_info(gZlog, "push strain data Index[%d] into strainBuf[%d], size[%d]", i, strainBufFlag, (i + 1));
    }else{
      CircularBufferPush(strainCircBuf2, &strainInfo[i], sizeof(strainInfo_t));
      if(i == (nodeDataCnt - 1)){
        strainBufFlag     = false;
        strainRecordFlag  = true;
      }
      zlog_info(gZlog, "push strain data Index[%d] into strainBuf[%d], size[%d]", i, strainBufFlag, (i + 1));
    }
    zlog_info(gZlog, "recieve strain data count[%d]", gStrainDataCnt++);
  }
  // clear gDevMsg
  memset(&gDevMsg, 0, sizeof(gDevMsg));
  return 0;
}
unsigned int gVibrationDataCnt;
int requirGetVibration(int devId, vibrationInfo_t *vibrationInfo, int nodeDataCnt)
{
  int ret = -1;
  linkList_t *linkNode = NULL;
  linkNode = findLinkNodeFromList(gServerLinkHead, devId);
  //1. 发送命令帧
  ret = proFramesSend(&(linkNode->link.chan), NULL, 0, \
          SPEC_OP1_GET_VIBRATION, nodeDataCnt, 0);
  if(ret < 0){
    zlog_error(gZlog, "failed to request vibration info from device[%d], ret[%d]", devId, ret);
    return ret;
  }
  //2. 等待响应帧
  ret = waitAck(ACK_TIMEOUT_MS);
  if(ret < 0){
    zlog_error(gZlog, "request vibration info from device[%d] timeout", devId);
    return ret;
  }
  //3. 处理接收内容
  if( gDevMsg.chanId != devId && gDevMsg.op1 != SPEC_OP1_GET_VIBRATION){
    zlog_error(gZlog, "recv vibration info from device[%d] ack failed", devId);
    return -1;
  }
  vibrationInfo_t getVibrationInfo;
  for(int i = 0; i < nodeDataCnt; i++){
    memcpy(&getVibrationInfo, (gDevMsg.payload + i * sizeof(vibrationInfo_t)), sizeof(vibrationInfo_t));
    vibrationInfo[i].timeStamp.tv_sec  = ntohl((getVibrationInfo.timeStamp.tv_sec & 0xffffffff00000000) >> 32) + ntohl(getVibrationInfo.timeStamp.tv_sec & 0xffffffff);
    vibrationInfo[i].timeStamp.tv_usec = ntohl((getVibrationInfo.timeStamp.tv_usec & 0xffffffff00000000) >> 32) + ntohl(getVibrationInfo.timeStamp.tv_usec & 0xffffffff);
    u32EndianSwap((uint32_t*)&getVibrationInfo.devId);
    u32EndianSwap((uint32_t*)&getVibrationInfo.temperature);
    u32EndianSwap((uint32_t*)&getVibrationInfo.accSpeedX);
    u32EndianSwap((uint32_t*)&getVibrationInfo.accSpeedY);
    u32EndianSwap((uint32_t*)&getVibrationInfo.accSpeedZ);
    u32EndianSwap((uint32_t*)&getVibrationInfo.angleX);
    u32EndianSwap((uint32_t*)&getVibrationInfo.angleY);
    u32EndianSwap((uint32_t*)&getVibrationInfo.angleZ);
    vibrationInfo[i].devId             = getVibrationInfo.devId;
    vibrationInfo[i].accSpeedX         = getVibrationInfo.accSpeedX;
    vibrationInfo[i].accSpeedY         = getVibrationInfo.accSpeedY;
    vibrationInfo[i].accSpeedZ         = getVibrationInfo.accSpeedZ;
    vibrationInfo[i].angleX            = getVibrationInfo.angleX;
    vibrationInfo[i].angleY            = getVibrationInfo.angleY;
    vibrationInfo[i].angleZ            = getVibrationInfo.angleZ;
    //4. print info
    struct tm fmtTime = {0};
    localtime_r(&(vibrationInfo[i].timeStamp.tv_sec), &fmtTime);
    char timeStampStr[32] = {0};
    strftime(timeStampStr, sizeof(timeStampStr), "%Y-%m-%d %H:%M:%S", &fmtTime);
    sprintf(&timeStampStr[19], ".%ld", vibrationInfo[i].timeStamp.tv_usec / 1000);
    zlog_info(gZlog, "\nrecv vibration acqNode[%d] data:\n\
      time stamp.........[%s], \n\
      accSpeedX value....[%.4f], \n\
      accSpeedY value....[%.4f], \n\
      accSpeedZ value....[%.4f], \n\
      angleX value.......[%.4f], \n\
      angleY value.......[%.4f], \n\
      angleZ value.......[%.4f]", \
      vibrationInfo[i].devId, timeStampStr, vibrationInfo[i].accSpeedX, \
      vibrationInfo[i].accSpeedY, vibrationInfo[i].accSpeedZ, \
      vibrationInfo[i].angleX, vibrationInfo[i].angleY, vibrationInfo[i].angleZ);
    //5. record vinration info
    if(vibrationBufFlag){
      CircularBufferPush(vibrationCircBuf1, &vibrationInfo[i], sizeof(vibrationInfo_t));
      if(i == (nodeDataCnt - 1)){
        vibrationBufFlag    = true;
        vibrationRecordFlag = true;
      }
    }else{
      CircularBufferPush(vibrationCircBuf2, &vibrationInfo[i], sizeof(vibrationInfo_t));
      if(i == (nodeDataCnt - 1)){
        vibrationBufFlag    = false;
        vibrationRecordFlag = true;
      }
    }
  zlog_info(gZlog, "recieve vibration data count[%d]", gVibrationDataCnt++);
  }
  memset(&gDevMsg, 0, sizeof(gDevMsg));
  return 0;
}

int testStrain(int devId)
{
  int ret = -1;
  linkList_t *linkNode = NULL;
  linkNode = findLinkNodeFromList(gServerLinkHead, devId);
  //1. data prepare
  strainInfo_t strainInfo;
  struct timeval now;
  gettimeofday(&now, NULL);
  struct tm fmtTime = {0};
  localtime_r(&(now.tv_sec), &fmtTime);
  char timeStampStr[32] = {0};
  strftime(timeStampStr, sizeof(timeStampStr), "%Y-%m-%d %H:%M:%S", &fmtTime);
  sprintf(&timeStampStr[19], ".%ld", strainInfo.timeStamp.tv_usec / 1000);
  strainInfo.devId             = 1;
  strainInfo.strainAdCh1       = 3.14;
  strainInfo.strainAdCh2       = 3.15;
  strainInfo.strainVal         = 3.16;
  zlog_info(gZlog, "\nrecv strain acqNode[%d] data:, \
      sizeof(float)......[%d], \
      time stamp.........[%s], \
      strain AD CH1 Val..[%.4f], \
      strain AD CH2 Val..[%.4f], \
      strain value.......[%.4f]", \
      strainInfo.devId, sizeof(float), timeStampStr, \
      strainInfo.strainAdCh1, strainInfo.strainAdCh2, strainInfo.strainVal);
  zlog_info(gZlog, "AD CH1 Hex htonl[0x%x]", (int)strainInfo.strainAdCh1);
  // 2. htoh
  strainInfo.timeStamp.tv_sec  = htonl(now.tv_sec);
  strainInfo.timeStamp.tv_usec = htonl(now.tv_usec);
  u32EndianSwap((uint32_t*)&strainInfo.strainAdCh1);
  u32EndianSwap((uint32_t*)&strainInfo.strainAdCh2);
  u32EndianSwap((uint32_t*)&strainInfo.strainVal);
  zlog_info(gZlog, "AD CH1 Hex htonl[0x%x]", (int)strainInfo.strainAdCh1);
  //3. 发送命令帧
  ret = proFramesSend(&(linkNode->link.chan), (char *)&strainInfo, sizeof(strainInfo_t), \
          SPEC_OP1_GET_STRAIN, 0, 0);
  if(ret < 0){
    zlog_error(gZlog, "failed to request strain info from device[%d], ret[%d]", devId, ret);
    return ret;
  }
  
  return 0;
}

int testVibration(int devId)
{
  int ret = -1;
  linkList_t *linkNode = NULL;
  linkNode = findLinkNodeFromList(gServerLinkHead, devId);
  //1. data prepare
  vibrationInfo_t vibrationInfo;
  struct timeval now;
  gettimeofday(&now, NULL);
  struct tm fmtTime = {0};
  localtime_r(&(now.tv_sec), &fmtTime);
  char timeStampStr[32] = {0};
  strftime(timeStampStr, sizeof(timeStampStr), "%Y-%m-%d %H:%M:%S", &fmtTime);
  sprintf(&timeStampStr[19], ".%ld", now.tv_usec / 1000);
  vibrationInfo.devId             = 1;
  vibrationInfo.accSpeedX         = 3.11;
  vibrationInfo.accSpeedY         = 3.12;
  vibrationInfo.accSpeedZ         = 3.13;
  vibrationInfo.angleX            = 3.14;
  vibrationInfo.angleY            = 3.15;
  vibrationInfo.angleZ            = 3.16;
  zlog_info(gZlog, "\nrecv vibration acqNode[%d] data:\
      time stamp............[%s] \
      accSpeedX value.......[%.4f]\
      accSpeedY value.......[%.4f]\
      accSpeedZ value.......[%.4f]\
      angleX value..........[%.4f]\
      angleY value..........[%.4f]\
      angleZ value..........[%.4f]", \
      vibrationInfo.devId, timeStampStr, \
      vibrationInfo.accSpeedX, vibrationInfo.accSpeedY, \
      vibrationInfo.accSpeedZ, vibrationInfo.angleX, \
      vibrationInfo.angleY, vibrationInfo.angleZ);
  // 2. htoh
  vibrationInfo.timeStamp.tv_sec      = htonl(now.tv_sec);
  vibrationInfo.timeStamp.tv_usec     = htonl(now.tv_usec);
  u32EndianSwap((uint32_t*)&vibrationInfo.accSpeedX);
  u32EndianSwap((uint32_t*)&vibrationInfo.accSpeedY);
  u32EndianSwap((uint32_t*)&vibrationInfo.accSpeedZ);
  u32EndianSwap((uint32_t*)&vibrationInfo.angleX);
  u32EndianSwap((uint32_t*)&vibrationInfo.angleY);
  u32EndianSwap((uint32_t*)&vibrationInfo.angleZ);
  //3. 发送命令帧
  ret = proFramesSend(&(linkNode->link.chan), (char *)&vibrationInfo, sizeof(vibrationInfo_t), \
          SPEC_OP1_GET_VIBRATION, 0, 0);
  if(ret < 0){
    zlog_error(gZlog, "failed to request vibration info from device[%d], ret[%d]", devId, ret);
    return ret;
  }
  
  return 0;
}

